package com.quanxiaoha.weblog.web.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.google.common.collect.Lists;
import com.quanxiaoha.weblog.common.domain.dos.AiChatRecordDO;
import com.quanxiaoha.weblog.common.domain.dos.ArticleDO;
import com.quanxiaoha.weblog.common.domain.dos.ArticleCategoryRelDO;
import com.quanxiaoha.weblog.common.domain.dos.ArticleTagRelDO;
import com.quanxiaoha.weblog.common.domain.dos.ArticleContentDO;
import com.quanxiaoha.weblog.common.domain.dos.CategoryDO;
import com.quanxiaoha.weblog.common.domain.dos.TagDO;
import com.quanxiaoha.weblog.common.domain.mapper.AiChatRecordMapper;
import com.quanxiaoha.weblog.common.domain.mapper.ArticleMapper;
import com.quanxiaoha.weblog.common.domain.mapper.ArticleContentMapper;
import com.quanxiaoha.weblog.common.domain.mapper.ArticleCategoryRelMapper;
import com.quanxiaoha.weblog.common.domain.mapper.ArticleTagRelMapper;
import com.quanxiaoha.weblog.common.domain.mapper.CategoryMapper;
import com.quanxiaoha.weblog.common.domain.mapper.TagMapper;
import com.quanxiaoha.weblog.common.utils.Response;
import com.quanxiaoha.weblog.web.model.dto.ai.OpenAiChatRequestDTO;
import com.quanxiaoha.weblog.web.model.dto.ai.OpenAiChatResponseDTO;
import com.quanxiaoha.weblog.web.model.vo.ai.AiChatHistoryRspVO;
import com.fasterxml.jackson.core.type.TypeReference;
import com.quanxiaoha.weblog.web.model.vo.ai.AiChatReqVO;
import com.quanxiaoha.weblog.web.model.vo.ai.AiChatRspVO;
import com.quanxiaoha.weblog.web.model.vo.ai.CreateNewConversationReqVO;
import com.quanxiaoha.weblog.web.model.vo.ai.CreateNewConversationRspVO;
import com.quanxiaoha.weblog.web.model.vo.ai.DeleteAiConversationReqVO;
import com.quanxiaoha.weblog.web.model.vo.ai.GetAiChatHistoryReqVO;
import com.quanxiaoha.weblog.web.model.vo.ai.GetConversationListReqVO;
import com.quanxiaoha.weblog.web.model.vo.ai.GetConversationListRspVO;
import com.quanxiaoha.weblog.web.model.vo.ai.ArticleWithMetadataVO;
import com.quanxiaoha.weblog.web.model.vo.ai.TextSelectionAiReqVO;
import com.quanxiaoha.weblog.web.model.vo.ai.TextSelectionAiRspVO;
import com.quanxiaoha.weblog.web.service.AiService;
import com.quanxiaoha.weblog.web.utils.AiApiUtil;
import com.quanxiaoha.weblog.web.utils.ArticleRecommendUtil;
import com.quanxiaoha.weblog.web.service.AiConfigService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.nio.charset.StandardCharsets;
import java.io.OutputStream;

/**
 * @author: 犬小哈
 * @url: www.quanxiaoha.com
 * @date: 2024-01-15
 * @description: AI助手服务实现
 **/
@Service
@Slf4j
public class AiServiceImpl implements AiService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private AiChatRecordMapper aiChatRecordMapper;

    @Autowired
    private ArticleCategoryRelMapper articleCategoryRelMapper;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private ArticleTagRelMapper articleTagRelMapper;

    @Autowired
    private TagMapper tagMapper;

    @Autowired
    private ArticleContentMapper articleContentMapper;

    @Autowired
    private AiApiUtil aiApiUtil;

    @Autowired
    private ArticleRecommendUtil articleRecommendUtil;

    @Autowired
    private AiConfigService aiConfigService;

    private final ObjectMapper objectMapper = new ObjectMapper();

    // 安全写入SSE消息，客户端断开时返回false并停止推送
    private boolean tryWrite(OutputStream outputStream, String data) {
        try {
            outputStream.write(data.getBytes(StandardCharsets.UTF_8));
            outputStream.flush();
            return true;
        } catch (Exception e) {
            log.warn("客户端已断开连接，停止SSE推送");
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response chat(AiChatReqVO aiChatReqVO) {
        String message = aiChatReqVO.getMessage();
        String conversationId = aiChatReqVO.getConversationId();

        // 如果没有对话ID，生成一个新的
        if (StringUtils.isBlank(conversationId)) {
            conversationId = UUID.randomUUID().toString();
        }

        try {
            // 保存用户消息到数据库
            saveMessageToDatabase(conversationId, "user", message, null);
            
            // 获取对话历史
            List<AiChatRecordDO> historyRecords = getConversationHistory(conversationId);
            List<OpenAiChatRequestDTO.Message> history = convertToMessages(historyRecords);

            // 生成AI回复
            String reply = generateAiReply(message, history);
            
            // 如果AI调用失败，使用备用回复
            if (StringUtils.isBlank(reply)) {
                reply = generateFallbackReply(message);
            }

            // 获取相关文章推荐
            List<AiChatRspVO.ArticleRecommendVO> recommendArticles = getRecommendArticles(message);

            // 保存AI回复到数据库（包含推荐文章）
            saveMessageToDatabase(conversationId, "assistant", reply, recommendArticles);

            AiChatRspVO rspVO = AiChatRspVO.builder()
                    .reply(reply)
                    .conversationId(conversationId)
                    .recommendArticles(recommendArticles)
                    .build();

            return Response.success(rspVO);
            
        } catch (Exception e) {
            log.error("AI聊天处理异常", e);
            
            // 异常情况下返回友好提示
            AiChatRspVO rspVO = AiChatRspVO.builder()
                    .reply("抱歉，我现在遇到了一些技术问题，请稍后再试。如果问题持续存在，您可以尝试刷新页面或联系管理员。")
                    .conversationId(conversationId)
                    .recommendArticles(Lists.newArrayList())
                    .build();
            
            return Response.success(rspVO);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response deleteConversation(DeleteAiConversationReqVO deleteAiConversationReqVO) {
        String conversationId = deleteAiConversationReqVO.getConversationId();
        
        try {
            // 软删除数据库中的聊天记录
            LambdaUpdateWrapper<AiChatRecordDO> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(AiChatRecordDO::getConversationId, conversationId)
                        .set(AiChatRecordDO::getIsDeleted, true)
                        .set(AiChatRecordDO::getUpdateTime, LocalDateTime.now());
            
            aiChatRecordMapper.update(null, updateWrapper);
            
            log.info("删除对话成功，对话ID: {}", conversationId);
            return Response.success();
        } catch (Exception e) {
            log.error("删除对话失败，对话ID: {}", conversationId, e);
            return Response.fail("删除对话失败");
        }
    }

    @Override
    public Response getChatHistory(GetAiChatHistoryReqVO getAiChatHistoryReqVO) {
        String conversationId = getAiChatHistoryReqVO.getConversationId();
        
        try {
            // 从数据库获取聊天历史记录
            List<AiChatRecordDO> records = getConversationHistory(conversationId);
            
            // 转换为响应VO
            List<AiChatHistoryRspVO.ChatMessage> messages = records.stream()
                    .map(record -> {
                        // 解析推荐文章
                        List<AiChatRspVO.ArticleRecommendVO> recommendArticles = null;
                        if (StringUtils.isNotBlank(record.getRecommendArticles())) {
                            try {
                                recommendArticles = objectMapper.readValue(
                                    record.getRecommendArticles(),
                                    new TypeReference<List<AiChatRspVO.ArticleRecommendVO>>() {}
                                );
                            } catch (Exception e) {
                                log.warn("解析推荐文章失败: {}", e.getMessage());
                                recommendArticles = Lists.newArrayList();
                            }
                        }

                        return AiChatHistoryRspVO.ChatMessage.builder()
                                .id(record.getId())
                                .messageType(record.getMessageType())
                                .content(record.getContent())
                                .createTime(record.getCreateTime())
                                .isUser("user".equals(record.getMessageType()))
                                .recommendArticles(recommendArticles)
                                .build();
                    })
                    .collect(Collectors.toList());
            
            AiChatHistoryRspVO rspVO = AiChatHistoryRspVO.builder()
                    .conversationId(conversationId)
                    .messages(messages)
                    .build();
            
            return Response.success(rspVO);
        } catch (Exception e) {
            log.error("获取聊天历史记录失败，对话ID: {}", conversationId, e);
            return Response.fail("获取聊天历史记录失败");
        }
    }

    /**
     * 保存消息到数据库
     */
    private void saveMessageToDatabase(String conversationId, String messageType, String content, List<AiChatRspVO.ArticleRecommendVO> recommendArticles) {
        try {
            String recommendArticlesJson = null;
            if (recommendArticles != null && !recommendArticles.isEmpty()) {
                recommendArticlesJson = objectMapper.writeValueAsString(recommendArticles);
            }

            AiChatRecordDO record = AiChatRecordDO.builder()
                    .conversationId(conversationId)
                    .messageType(messageType)
                    .content(content)
                    .recommendArticles(recommendArticlesJson)
                    .createTime(LocalDateTime.now())
                    .updateTime(LocalDateTime.now())
                    .isDeleted(false)
                    .build();

            aiChatRecordMapper.insert(record);
            log.info("保存消息成功，对话ID: {}, 消息类型: {}, 推荐文章数: {}",
                    conversationId, messageType, recommendArticles != null ? recommendArticles.size() : 0);
        } catch (Exception e) {
            log.error("保存消息失败", e);
            throw new RuntimeException("保存消息失败", e);
        }
    }

    /**
     * 获取对话历史记录
     */
    private List<AiChatRecordDO> getConversationHistory(String conversationId) {
        LambdaQueryWrapper<AiChatRecordDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiChatRecordDO::getConversationId, conversationId)
                    .eq(AiChatRecordDO::getIsDeleted, false)
                    .orderByAsc(AiChatRecordDO::getCreateTime);
        
        return aiChatRecordMapper.selectList(queryWrapper);
    }

    /**
     * 转换数据库记录为AI消息格式
     */
    private List<OpenAiChatRequestDTO.Message> convertToMessages(List<AiChatRecordDO> records) {
        return records.stream()
                .map(record -> OpenAiChatRequestDTO.Message.builder()
                        .role(record.getMessageType())
                        .content(record.getContent())
                        .build())
                .collect(Collectors.toList());
    }

    /**
     * 生成AI回复
     */
    private String generateAiReply(String message, List<OpenAiChatRequestDTO.Message> history) {
        try {
            // 构建系统提示
            String systemPrompt = buildSystemPrompt();
            
            // 构建完整的消息列表（系统提示 + 历史对话 + 当前消息）
            List<OpenAiChatRequestDTO.Message> messages = Lists.newArrayList();
            
            // 添加系统提示
            messages.add(OpenAiChatRequestDTO.Message.builder()
                    .role("system")
                    .content(systemPrompt)
                    .build());
            
            // 添加历史对话（只保留最近10条，避免token过多）
            int startIndex = Math.max(0, history.size() - 10);
            messages.addAll(history.subList(startIndex, history.size()));
            
            // 获取动态AI配置
            com.quanxiaoha.weblog.web.config.AiConfig aiConfig = aiConfigService.getCurrentAiConfig();
            
            // 构建请求
            OpenAiChatRequestDTO request = OpenAiChatRequestDTO.builder()
                    .model(aiConfig.getModel())
                    .temperature(0.7)
                    .stream(false)
                    .messages(messages)
                    .build();
            
            // 调用AI API
            OpenAiChatResponseDTO response = aiApiUtil.chatCompletion(request);
            
            // 提取回复内容
            String reply = aiApiUtil.extractReplyContent(response);
            
            if (StringUtils.isNotBlank(reply)) {
                log.info("AI回复成功: {}", reply);
                return reply;
            } else {
                log.warn("AI回复为空，使用备用回复");
                return null;
            }
            
        } catch (Exception e) {
            log.error("调用AI API异常", e);
            return null;
        }
    }

    /**
     * 构建系统提示
     */
    private String buildSystemPrompt() {
        return "你是一个专业的技术博客AI助手，具有以下特点和能力：\n\n" +
               "## 角色定位\n" +
               "- 你是一个友好、专业的技术助手\n" +
               "- 专注于回答编程、开发、技术学习相关的问题\n" +
               "- 能够为用户提供准确、实用的技术建议\n\n" +
               "## 回答风格\n" +
               "- 使用简洁、清晰的中文回答\n" +
               "- 回答要有条理性，必要时使用编号或分点说明\n" +
               "- 语气要友好、耐心，避免过于技术化的术语\n" +
               "- 回答长度控制在200-500字之间，支持markdown格式\n\n" +
               "## 技术领域\n" +
               "主要涵盖以下技术领域：\n" +
               "- Java、Spring Boot、Spring Cloud等后端技术\n" +
               "- Vue.js、React、前端开发技术\n" +
               "- 数据库设计与优化（MySQL、Redis等）\n" +
               "- 微服务架构、分布式系统\n" +
               "- DevOps、部署运维\n" +
               "- 编程基础、算法数据结构\n\n" +
               "## 回答原则\n" +
               "1. 如果问题涉及具体代码，提供简洁的示例代码，使用```代码块```格式\n" +
               "2. 对于学习问题，给出循序渐进的建议\n" +
               "3. 对于技术选型，说明优缺点和适用场景\n" +
               "4. 如果不确定，诚实说明并建议查阅官方文档\n" +
               "5. 鼓励用户实践和动手操作\n" +
               "6. 支持使用markdown格式，如**加粗**、*斜体*、`代码`等\n\n" +
               "请根据用户的问题，提供专业、实用的技术建议，支持富文本markdown格式。";
    }

    /**
     * 生成备用回复（当AI API调用失败时使用）
     */
    private String generateFallbackReply(String message) {
        String lowerMessage = message.toLowerCase();
        
        if (lowerMessage.contains("你好") || lowerMessage.contains("hello") || lowerMessage.contains("hi")) {
            return "你好！我是博客AI助手，很高兴为您服务！我可以帮您回答技术问题、推荐相关文章，或者协助您查找博客内容。有什么我可以帮助您的吗？";
        }
        
        if (lowerMessage.contains("技术") || lowerMessage.contains("编程") || lowerMessage.contains("代码")) {
            return "关于技术和编程方面的问题，我很乐意为您解答！这个博客包含了很多技术文章和编程经验分享。您具体想了解哪个方面的技术呢？比如**Java**、**Spring Boot**、**Vue.js**、**数据库**等？";
        }
        
        if (lowerMessage.contains("spring") || lowerMessage.contains("java")) {
            return "**Java**和**Spring**是非常流行的后端技术栈！\n\n- **Spring Boot**特别适合快速开发企业级应用\n- **Spring Cloud**用于微服务架构\n- **Spring Security**提供安全认证\n\n您是想了解Spring的具体使用方法，还是遇到了什么技术问题呢？我可以为您推荐一些相关的技术文章。";
        }
        
        if (lowerMessage.contains("vue") || lowerMessage.contains("前端") || lowerMessage.contains("javascript")) {
            return "**Vue.js**是一个优秀的前端框架！特别是Vue 3的Composition API，让代码更加简洁和易维护。\n\n主要特点：\n- 📦 **组件化开发**\n- 🚀 **响应式数据绑定**\n- 🎯 **单文件组件**\n- ⚡ **性能优异**\n\n您是在学习Vue开发，还是遇到了具体的前端问题呢？";
        }
        
        // 默认回复
        return "感谢您的提问！我会尽力帮助您解答技术问题。您可以：\n\n" +
               "1. 📝 描述您遇到的具体技术问题\n" +
               "2. 📚 告诉我您想学习的技术方向\n" +
               "3. 🔍 询问关于博客内容的相关问题\n\n" +
               "我会根据您的问题为您提供相应的建议和推荐相关文章！";
    }

    /**
     * 获取推荐文章（使用增强的智能推荐算法）
     */
    private List<AiChatRspVO.ArticleRecommendVO> getRecommendArticles(String message) {
        try {
            // 获取包含元数据的文章列表
            List<ArticleWithMetadataVO> articlesWithMetadata = getArticlesWithMetadata();

            if (CollectionUtils.isEmpty(articlesWithMetadata)) {
                return Lists.newArrayList();
            }

            // 使用AI增强用户查询
            String enhancedMessage = aiApiUtil.enhanceQueryWithAi(message);
            log.info("AI增强查询 - 原始: [{}], 增强后: [{}]", message, enhancedMessage);

            // 使用增强的智能推荐算法
            List<ArticleRecommendUtil.ArticleRecommendResult> recommendResults =
                    articleRecommendUtil.recommendArticles(enhancedMessage, articlesWithMetadata, 3);

            // 如果增强查询没有结果，尝试使用原始查询
            if (recommendResults.isEmpty() && !enhancedMessage.equals(message)) {
                log.info("AI增强查询无结果，回退到原始查询");
                recommendResults = articleRecommendUtil.recommendArticles(message, articlesWithMetadata, 3);
            }

            // 转换为响应VO
            return recommendResults.stream()
                    .map(result -> {
                        ArticleWithMetadataVO article = result.getArticle();
                        return AiChatRspVO.ArticleRecommendVO.builder()
                                .id(article.getId())
                                .title(article.getTitle())
                                .summary(article.getSummary())
                                .createTime(article.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
                                .similarity(Math.round(result.getSimilarity() * 100.0) / 100.0) // 保留两位小数
                                .matchKeywords(result.getMatchKeywords())
                                .build();
                    })
                    .collect(Collectors.toList());

        } catch (Exception e) {
            log.error("获取推荐文章失败", e);
            return Lists.newArrayList();
        }
    }

    /**
     * 获取包含元数据的文章列表
     */
    private List<ArticleWithMetadataVO> getArticlesWithMetadata() {
        try {
            // 获取所有文章
            List<ArticleDO> articles = articleMapper.selectList(null);

            if (CollectionUtils.isEmpty(articles)) {
                return Lists.newArrayList();
            }

            List<Long> articleIds = articles.stream().map(ArticleDO::getId).collect(Collectors.toList());

            // 批量获取文章分类关联
            List<ArticleCategoryRelDO> categoryRels = articleCategoryRelMapper.selectByArticleIds(articleIds);
            Map<Long, Long> articleCategoryMap = categoryRels.stream()
                    .collect(Collectors.toMap(ArticleCategoryRelDO::getArticleId, ArticleCategoryRelDO::getCategoryId));

            // 批量获取分类信息
            Set<Long> categoryIds = new HashSet<>(articleCategoryMap.values());
            Map<Long, String> categoryNameMap = new HashMap<>();
            if (!categoryIds.isEmpty()) {
                List<CategoryDO> categories = categoryMapper.selectBatchIds(categoryIds);
                categoryNameMap = categories.stream()
                        .collect(Collectors.toMap(CategoryDO::getId, CategoryDO::getName));
            }

            // 批量获取文章标签关联
            List<ArticleTagRelDO> tagRels = articleTagRelMapper.selectByArticleIds(articleIds);
            Map<Long, List<Long>> articleTagsMap = tagRels.stream()
                    .collect(Collectors.groupingBy(
                            ArticleTagRelDO::getArticleId,
                            Collectors.mapping(ArticleTagRelDO::getTagId, Collectors.toList())
                    ));

            // 批量获取标签信息
            Set<Long> tagIds = tagRels.stream().map(ArticleTagRelDO::getTagId).collect(Collectors.toSet());
            Map<Long, String> tagNameMap = new HashMap<>();
            if (!tagIds.isEmpty()) {
                List<TagDO> tags = tagMapper.selectBatchIds(tagIds);
                tagNameMap = tags.stream()
                        .collect(Collectors.toMap(TagDO::getId, TagDO::getName));
            }

            // 批量获取文章内容
            Map<Long, String> articleContentMap = new HashMap<>();
            for (Long articleId : articleIds) {
                ArticleContentDO contentDO = articleContentMapper.selectByArticleId(articleId);
                if (contentDO != null && StringUtils.isNotBlank(contentDO.getContent())) {
                    // 截取内容前1000个字符用于推荐分析，避免内容过长影响性能
                    String content = contentDO.getContent();
                    if (content.length() > 1000) {
                        content = content.substring(0, 1000);
                    }
                    // 移除HTML标签，只保留纯文本
                    content = content.replaceAll("<[^>]*>", "").trim();
                    articleContentMap.put(articleId, content);
                }
            }

            // 组装文章元数据
            final Map<Long, String> finalCategoryNameMap = categoryNameMap;
            final Map<Long, String> finalTagNameMap = tagNameMap;
            final Map<Long, String> finalArticleContentMap = articleContentMap;

            return articles.stream()
                    .map(article -> {
                        // 获取分类名称
                        Long categoryId = articleCategoryMap.get(article.getId());
                        String categoryName = categoryId != null ? finalCategoryNameMap.get(categoryId) : null;

                        // 获取标签名称列表
                        List<Long> tagIdList = articleTagsMap.get(article.getId());
                        List<String> tagNames = Lists.newArrayList();
                        if (tagIdList != null) {
                            tagNames = tagIdList.stream()
                                    .map(finalTagNameMap::get)
                                    .filter(Objects::nonNull)
                                    .collect(Collectors.toList());
                        }

                        return ArticleWithMetadataVO.builder()
                                .id(article.getId())
                                .title(article.getTitle())
                                .summary(article.getSummary())
                                .content(finalArticleContentMap.get(article.getId()))
                                .createTime(article.getCreateTime())
                                .readNum(article.getReadNum())
                                .weight(article.getWeight())
                                .categoryName(categoryName)
                                .tagNames(tagNames)
                                .build();
                    })
                    .collect(Collectors.toList());

        } catch (Exception e) {
            log.error("获取文章元数据失败", e);
            return Lists.newArrayList();
        }
    }

    @Override
    public Response createNewConversation(CreateNewConversationReqVO createNewConversationReqVO) {
        try {
            // 生成新的对话ID
            String conversationId = UUID.randomUUID().toString();

            // 生成对话标题
            String title = StringUtils.isNotBlank(createNewConversationReqVO.getTitle())
                    ? createNewConversationReqVO.getTitle()
                    : "新对话";

            LocalDateTime now = LocalDateTime.now();

            CreateNewConversationRspVO rspVO = CreateNewConversationRspVO.builder()
                    .conversationId(conversationId)
                    .title(title)
                    .createTime(now)
                    .build();

            log.info("创建新对话成功，对话ID: {}, 标题: {}", conversationId, title);
            return Response.success(rspVO);

        } catch (Exception e) {
            log.error("创建新对话失败", e);
            return Response.fail("创建新对话失败");
        }
    }

    @Override
    public Response getConversationList(GetConversationListReqVO getConversationListReqVO) {
        try {
            // 查询所有未删除的聊天记录
            LambdaQueryWrapper<AiChatRecordDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiChatRecordDO::getIsDeleted, false)
                        .orderByDesc(AiChatRecordDO::getUpdateTime);

            List<AiChatRecordDO> allRecords = aiChatRecordMapper.selectList(queryWrapper);

            if (CollectionUtils.isEmpty(allRecords)) {
                GetConversationListRspVO rspVO = GetConversationListRspVO.builder()
                        .conversations(Lists.newArrayList())
                        .total(0L)
                        .build();
                return Response.success(rspVO);
            }

            // 按对话ID分组
            Map<String, List<AiChatRecordDO>> conversationMap = allRecords.stream()
                    .collect(Collectors.groupingBy(AiChatRecordDO::getConversationId));

            // 构建对话列表
            List<GetConversationListRspVO.ConversationItem> conversations = conversationMap.entrySet().stream()
                    .map(entry -> {
                        String conversationId = entry.getKey();
                        List<AiChatRecordDO> records = entry.getValue();

                        // 按时间排序
                        records.sort((a, b) -> a.getCreateTime().compareTo(b.getCreateTime()));

                        // 获取第一条用户消息作为标题
                        String title = records.stream()
                                .filter(record -> "user".equals(record.getMessageType()))
                                .findFirst()
                                .map(record -> {
                                    String content = record.getContent();
                                    return content.length() > 20 ? content.substring(0, 20) + "..." : content;
                                })
                                .orElse("新对话");

                        // 获取最后一条消息
                        AiChatRecordDO lastRecord = records.get(records.size() - 1);
                        String lastMessage = lastRecord.getContent();
                        if (lastMessage.length() > 50) {
                            lastMessage = lastMessage.substring(0, 50) + "...";
                        }

                        return GetConversationListRspVO.ConversationItem.builder()
                                .conversationId(conversationId)
                                .title(title)
                                .lastMessage(lastMessage)
                                .lastUpdateTime(lastRecord.getUpdateTime())
                                .messageCount(records.size())
                                .build();
                    })
                    .sorted((a, b) -> b.getLastUpdateTime().compareTo(a.getLastUpdateTime()))
                    .collect(Collectors.toList());

            // 分页处理
            Integer pageNum = getConversationListReqVO.getPageNum();
            Integer pageSize = getConversationListReqVO.getPageSize();

            int total = conversations.size();
            int startIndex = (pageNum - 1) * pageSize;
            int endIndex = Math.min(startIndex + pageSize, total);

            List<GetConversationListRspVO.ConversationItem> pagedConversations =
                    startIndex < total ? conversations.subList(startIndex, endIndex) : Lists.newArrayList();

            GetConversationListRspVO rspVO = GetConversationListRspVO.builder()
                    .conversations(pagedConversations)
                    .total((long) total)
                    .build();

            log.info("获取对话列表成功，总数: {}, 当前页: {}", total, pageNum);
            return Response.success(rspVO);

        } catch (Exception e) {
            log.error("获取对话列表失败", e);
            return Response.fail("获取对话列表失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response textSelectionChat(TextSelectionAiReqVO textSelectionAiReqVO) {
        String selectedText = textSelectionAiReqVO.getSelectedText();
        String mode = textSelectionAiReqVO.getMode();
        String conversationId = textSelectionAiReqVO.getConversationId();
        Long articleId = textSelectionAiReqVO.getArticleId();

        // 如果没有对话ID，生成一个新的
        if (StringUtils.isBlank(conversationId)) {
            conversationId = UUID.randomUUID().toString();
        }

        try {
            // 构建基于模式的提示词
            String promptMessage = buildModePrompt(mode, selectedText, articleId,
                textSelectionAiReqVO.getSourceLanguage(), textSelectionAiReqVO.getTargetLanguage());

            // 保存用户消息到数据库
            saveMessageToDatabase(conversationId, "user", promptMessage, null);

            // 获取对话历史
            List<AiChatRecordDO> historyRecords = getConversationHistory(conversationId);
            List<OpenAiChatRequestDTO.Message> history = convertToMessages(historyRecords);

            // 生成AI回复
            String reply = generateAiReply(promptMessage, history);

            // 如果AI调用失败，使用备用回复
            if (StringUtils.isBlank(reply)) {
                reply = generateTextSelectionFallbackReply(mode, selectedText);
            }

            // 保存AI回复到数据库（划词功能不推荐文章）
            saveMessageToDatabase(conversationId, "assistant", reply, null);

            TextSelectionAiRspVO rspVO = TextSelectionAiRspVO.builder()
                    .reply(reply)
                    .conversationId(conversationId)
                    .mode(mode)
                    .originalText(selectedText)
                    .build();

            return Response.success(rspVO);

        } catch (Exception e) {
            log.error("文章划词AI交互处理异常", e);

            // 异常情况下返回友好提示
            TextSelectionAiRspVO rspVO = TextSelectionAiRspVO.builder()
                    .reply("抱歉，我现在遇到了一些技术问题，请稍后再试。")
                    .conversationId(conversationId)
                    .mode(mode)
                    .originalText(selectedText)
                    .build();

            return Response.success(rspVO);
        }
    }

    /**
     * 构建基于模式的提示词
     */
    private String buildModePrompt(String mode, String selectedText, Long articleId,
                                 String sourceLanguage, String targetLanguage) {
        String basePrompt;

        switch (mode) {
            case "explain":
                basePrompt = "请详细解释以下内容，包括其含义、背景和相关概念：\n\n";
                break;
            case "translate":
                basePrompt = buildTranslatePrompt(selectedText, sourceLanguage, targetLanguage);
                break;
            case "summarize":
                basePrompt = "请总结以下内容的要点和核心观点：\n\n";
                break;
            default:
                basePrompt = "请分析以下内容：\n\n";
        }

        return basePrompt + "\"" + selectedText + "\"";
    }

    /**
     * 构建翻译提示词
     */
    private String buildTranslatePrompt(String selectedText, String sourceLanguage, String targetLanguage) {
        // 如果指定了源语言和目标语言，使用指定的语言
        if (StringUtils.isNotBlank(sourceLanguage) && StringUtils.isNotBlank(targetLanguage)) {
            String sourceLangName = getLanguageName(sourceLanguage);
            String targetLangName = getLanguageName(targetLanguage);
            return String.format("请将以下%s内容翻译成%s：\n\n", sourceLangName, targetLangName);
        }

        // 如果只指定了目标语言，自动判断源语言
        if (StringUtils.isNotBlank(targetLanguage)) {
            String targetLangName = getLanguageName(targetLanguage);
            return String.format("请将以下内容翻译成%s：\n\n", targetLangName);
        }

        // 默认逻辑：自动判断中英文互译
        boolean containsChinese = selectedText.matches(".*[\\u4e00-\\u9fa5].*");
        if (containsChinese) {
            return "请将以下中文内容翻译成英文：\n\n";
        } else {
            return "请将以下英文内容翻译成中文：\n\n";
        }
    }

    /**
     * 获取语言名称
     */
    private String getLanguageName(String languageCode) {
        switch (languageCode.toLowerCase()) {
            case "zh":
            case "zh-cn":
                return "中文";
            case "en":
            case "en-us":
                return "英文";
            case "ja":
            case "jp":
                return "日文";
            case "ko":
            case "kr":
                return "韩文";
            case "fr":
                return "法文";
            case "de":
                return "德文";
            case "es":
                return "西班牙文";
            case "ru":
                return "俄文";
            case "it":
                return "意大利文";
            case "pt":
                return "葡萄牙文";
            default:
                return languageCode.toUpperCase();
        }
    }

    /**
     * 生成划词功能的备用回复
     */
    private String generateTextSelectionFallbackReply(String mode, String selectedText) {
        switch (mode) {
            case "explain":
                return "很抱歉，我暂时无法为您解释这段内容。这可能是一个技术概念或专业术语，建议您：\n\n" +
                       "1. 📚 查阅相关技术文档\n" +
                       "2. 🔍 在搜索引擎中搜索关键词\n" +
                       "3. 💬 在技术社区中提问\n\n" +
                       "如果是编程相关内容，我建议查看官方文档或技术博客。";

            case "translate":
                return "很抱歉，我暂时无法为您翻译这段内容。您可以：\n\n" +
                       "1. 🌐 使用在线翻译工具（如Google翻译、百度翻译）\n" +
                       "2. 📖 查阅专业词典\n" +
                       "3. 🤝 寻求专业翻译人员的帮助\n\n" +
                       "对于技术术语，建议保留英文原文以确保准确性。";

            case "summarize":
                return "很抱歉，我暂时无法为您总结这段内容。您可以尝试：\n\n" +
                       "1. 📝 手动提取关键信息\n" +
                       "2. 🎯 关注段落的主题句\n" +
                       "3. 📊 制作思维导图整理要点\n\n" +
                       "如果内容较长，建议分段阅读并逐步总结。";

            default:
                return "感谢您使用划词功能！我会尽力帮助您分析选中的内容。请稍后再试，或者尝试：\n\n" +
                       "1. 📝 重新选择文本\n" +
                       "2. 🔄 刷新页面后重试\n" +
                       "3. 💬 直接在聊天框中提问";
        }
    }

    @Override
    public ResponseEntity<StreamingResponseBody> streamChat(AiChatReqVO aiChatReqVO) {
        // 检查是否启用流式输出
        com.quanxiaoha.weblog.web.config.AiConfig aiConfig = aiConfigService.getCurrentAiConfig();
        if (!aiConfig.isStreamEnabled()) {
            // 如果未启用流式输出，返回错误响应
            StreamingResponseBody body = outputStream -> {
                String errorResponse = "data: {\"error\": \"流式输出未启用，请在管理后台开启流式输出功能\"}\n\n";
                tryWrite(outputStream, errorResponse);
            };
            return ResponseEntity.ok()
                    .header("Content-Type", "text/event-stream; charset=UTF-8")
                    .header("Cache-Control", "no-cache, no-transform")
                    .header("Connection", "keep-alive")
                    .header("X-Accel-Buffering", "no")
                    .body(body);
        }

        StreamingResponseBody responseBody = outputStream -> {
            try {
                String message = aiChatReqVO.getMessage();
                String conversationId = aiChatReqVO.getConversationId();

                // 如果没有对话ID，生成一个新的
                if (StringUtils.isBlank(conversationId)) {
                    conversationId = UUID.randomUUID().toString();
                }

                // 保存用户消息到数据库
                saveMessageToDatabase(conversationId, "user", message, null);

                // 发送开始信号
                String startData = "data: {\"type\": \"start\", \"conversationId\": \"" + conversationId + "\"}\n\n";
                if (!tryWrite(outputStream, startData)) return;

                // 获取对话历史
                List<AiChatRecordDO> historyRecords = getConversationHistory(conversationId);
                List<OpenAiChatRequestDTO.Message> history = convertToMessages(historyRecords);

                // 构建完整的消息列表（系统提示 + 历史对话）
                List<OpenAiChatRequestDTO.Message> messages = Lists.newArrayList();
                messages.add(OpenAiChatRequestDTO.Message.builder()
                        .role("system")
                        .content(buildSystemPrompt())
                        .build());
                int startIndex = Math.max(0, history.size() - 10);
                messages.addAll(history.subList(startIndex, history.size()));

                // 生成AI回复
                String reply = generateAiReply(message, history);

                // 如果AI调用失败，使用备用回复
                if (StringUtils.isBlank(reply)) {
                    reply = generateFallbackReply(message);
                }

                // 模拟流式输出效果（逐字发送）
                for (int i = 0; i < reply.length(); i++) {
                    char c = reply.charAt(i);
                    String chunkData = "data: {\"type\": \"chunk\", \"content\": \"" +
                            String.valueOf(c).replace("\"", "\\\"").replace("\n", "\\n") + "\"}\n\n";
                    if (!tryWrite(outputStream, chunkData)) return;
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }

                // 获取相关文章推荐
                List<AiChatRspVO.ArticleRecommendVO> recommendArticles = getRecommendArticles(message);

                // 保存AI回复到数据库
                saveMessageToDatabase(conversationId, "assistant", reply, recommendArticles);

                // 发送推荐文章（如果有）
                if (recommendArticles != null && !recommendArticles.isEmpty()) {
                    String articlesJson = objectMapper.writeValueAsString(recommendArticles);
                    String articleData = "data: {\"type\": \"articles\", \"articles\": " + articlesJson + "}\n\n";
                    if (!tryWrite(outputStream, articleData)) return;
                }

                // 发送结束信号
                String endData = "data: {\"type\": \"end\", \"conversationId\": \"" + conversationId + "\"}\n\n";
                tryWrite(outputStream, endData);
                log.info("流式AI聊天完成，对话ID: {}", conversationId);

            } catch (Exception e) {
                String msg = e.getMessage() != null ? e.getMessage() : "";
                if (e instanceof java.io.IOException || msg.contains("ClientAbort") || msg.contains("Broken pipe") || msg.contains("Failed write")) {
                    log.warn("流式连接已中断: {}", msg);
                } else {
                    log.error("流式聊天处理异常", e);
                }
            }
        };

        return ResponseEntity.ok()
                .header("Content-Type", "text/event-stream; charset=UTF-8")
                .header("Cache-Control", "no-cache, no-transform")
                .header("Connection", "keep-alive")
                .header("Access-Control-Allow-Origin", "*")
                .header("Access-Control-Allow-Headers", "Cache-Control")
                .header("X-Accel-Buffering", "no")
                .body(responseBody);
    }
}